package site.yunnong.atvris.portal.service.impl;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import site.yunnong.atvris.common.dto.*;
import site.yunnong.atvris.common.enumeration.MediaStatusEnum;
import site.yunnong.atvris.common.enumeration.MediaTypeEnum;
import site.yunnong.atvris.common.enumeration.VideoSortEnum;
import site.yunnong.atvris.common.utils.DateUtils;
import site.yunnong.atvris.common.utils.RedisKeyUtils;
import site.yunnong.atvris.dao.domain.Banner;
import site.yunnong.atvris.dao.domain.Video;
import site.yunnong.atvris.dao.domain.ext.VideoDetail;
import site.yunnong.atvris.dao.mapper.ext.VideoExtMapper;
import site.yunnong.atvris.portal.service.HomeService;
import site.yunnong.atvris.service.*;
import site.yunnong.atvris.service.vo.HeadAdVO;
import site.yunnong.atvris.service.vo.VideoCardVO;
import site.yunnong.atvris.service.vo.VideoRankVO;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zjh
 * @date 2021/7/12 0:54
 */
@Slf4j
@Service
public class HomeServiceImpl implements HomeService {

    @Resource
    private VideoService videoService;
    @Resource
    private BannerService bannerService;
    @Resource
    private VideoProfileService videoProfileService;
    @Resource
    private InteractionService interactionService;
    @Resource
    private VideoExtMapper videoExtMapper;
    @Resource
    private MediaPartitionService mediaPartitionService;
    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public List<HeadAdVO> headRecommendAdList(HeadRecommendAdParam param) {
        List<Banner> bannerList = bannerService.selectAllEffective();
        if (CollectionUtils.isEmpty(bannerList)) {
            return Lists.newArrayList();
        }
        // 随机抽pageSize个视频作为推送广告
        List<HeadAdVO> result = Lists.newArrayList();
        for (int i = 0; i < param.getPageSize(); i++) {
            int index = new Random().nextInt(bannerList.size() - 1);
            result.add(buildHeadAdVO(bannerList.get(index)));
        }
        return result;
    }

    private HeadAdVO buildHeadAdVO(Banner banner) {
        HeadAdVO vo = new HeadAdVO();
        BeanUtils.copyProperties(banner, vo);
        return vo;
    }

    @Override
    public List<VideoCardVO> headRecommendVideoList(HeadRecommendVideoParam param) {
        List<Video> videoList = videoService.selectRandomVideoList(param.getPageSize(), null);
        if (CollectionUtils.isEmpty(videoList)) {
            return Lists.newArrayList();
        }
        return videoList.stream().map(video -> {
            VideoCardVO vo = new VideoCardVO();
            BeanUtils.copyProperties(video, vo);
            vo.setLikedCount(interactionService.findMediaLikedCount(MediaTypeEnum.VIDEO.getValue(), video.getUvid()));
            vo.setViewCount(interactionService.findMediaViewCount(MediaTypeEnum.VIDEO.getValue(), video.getUvid()));
            vo.setDuration(DateUtils.covertSecondToDuration(video.getVideoTime()));
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<VideoCardVO> partitionRecommendVideoList(PartitionRecommendVideoParam param) {
        List<Video> videoList = videoService.selectRandomVideoList(param.getPageSize(), param.getPid());
        if (CollectionUtils.isEmpty(videoList)) {
            return Lists.newArrayList();
        }
        return videoList.stream().map(video -> {
            VideoCardVO vo = new VideoCardVO();
            BeanUtils.copyProperties(video, vo);
            vo.setLikedCount(interactionService.findMediaLikedCount(MediaTypeEnum.VIDEO.getValue(), video.getUvid()));
            vo.setViewCount(interactionService.findMediaViewCount(MediaTypeEnum.VIDEO.getValue(), video.getUvid()));
            vo.setDuration(DateUtils.covertSecondToDuration(video.getVideoTime()));
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public List<VideoRankVO> partitionVideoRankList(PartitionVideoRankParam param) {
        List<VideoRankVO> videoRankVOList = getCacheRank(param);
        if (CollectionUtils.isEmpty(videoRankVOList)) {
            videoRankVOList = initCacheRank(param);
        }
        return videoRankVOList;
    }

    private List<VideoRankVO> initCacheRank(PartitionVideoRankParam param) {
        String mediaScoreLockKey = RedisKeyUtils.getMediaScoreLockKey(MediaTypeEnum.VIDEO.getValue(), param.getPid());
        Long count = redisTemplate.opsForValue().increment(mediaScoreLockKey, 1);
        if (count == 1) {
            redisTemplate.expire(mediaScoreLockKey, 1000, TimeUnit.MILLISECONDS);
            VideoQueryParam videoQueryParam = new VideoQueryParam();
            videoQueryParam.setStatus(MediaStatusEnum.APPROVED.getValue());
            List<Long> pidList = mediaPartitionService.queryChildPidList(param.getPid());
            videoQueryParam.setPids(pidList);
            videoQueryParam.setStartIndex(0);
            videoQueryParam.setPageSize(param.getSize());
            videoQueryParam.setOrder(VideoSortEnum.SCORE_SORT.getValue());
            List<VideoDetail> videoDetails = videoExtMapper.queryUserVideoList(videoQueryParam);
            List<VideoRankVO> voList = Optional.of(videoDetails).orElse(Lists.newArrayList())
                    .stream().map(this::buildVideoRankVO).collect(Collectors.toList());
            Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
            if (CollectionUtils.isNotEmpty(voList)) {
                for (VideoRankVO videoRankVO : voList) {
                    DefaultTypedTuple<String> tuple = new DefaultTypedTuple<>(JSON.toJSONString(videoRankVO), videoRankVO.getScore());
                    tuples.add(tuple);
                }
            }
            String rankKey = RedisKeyUtils.getRankKey(param.getPid(), MediaTypeEnum.VIDEO.getValue());
            // 存入redis
            redisTemplate.opsForZSet().add(rankKey, tuples);
            // 一天过期
            redisTemplate.expire(rankKey, 1, TimeUnit.DAYS);
            return voList;
        }
        // 未获取到锁
        return Lists.newArrayList();
    }

    private List<VideoRankVO> getCacheRank(PartitionVideoRankParam param) {
        String rankKey = RedisKeyUtils.getRankKey(param.getPid(), MediaTypeEnum.VIDEO.getValue());
        Set<String> result = redisTemplate.opsForZSet().reverseRange(rankKey, 0, param.getSize());
        if (CollectionUtils.isEmpty(result)) {
            return Lists.newArrayList();
        }
        return result.stream().map(item -> JSON.parseObject(item, VideoRankVO.class)).collect(Collectors.toList());
    }

    private VideoRankVO buildVideoRankVO(VideoDetail videoDetail) {
        VideoRankVO vo = new VideoRankVO();
        BeanUtils.copyProperties(videoDetail, vo);
        return vo;
    }
}
